﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.IO;
using System.Windows.Forms;

namespace VBOX
{
    class VboxSerialPort
    {
        private SerialPort comPort = new SerialPort();

        public static int TxByte = 0;
        public static int RxByte = 0;

        //申明委托类型
        public delegate void UpdateTextEventHandler(byte[] revText);
        //使用委托类型申明委托函数
        public UpdateTextEventHandler updateTextBox;

        public static bool flag_close = false;//表示是否将要启动关闭流程
        public static bool Listening = false;//是否没有执行完invoke相关操作
        public static bool SpClosing = false;//是否正在关闭串口，执行Application.DoEvents，并阻止再次invoke
        
        public static bool HexRecvFlag = false;//16进制数据接收标志

        /// <summary>
        /// 参数构造函数（使用字符串参数构造）
        /// </summary>
        public VboxSerialPort()
        {
            comPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
        }

        /// <summary>
        /// 设置串口号
        /// </summary>
        /// <param name="obj"></param>
        public static void SetPortNameValues(ComboBox obj)
        {
            obj.Items.Clear();
            foreach (string str in SerialPort.GetPortNames())
            {
                obj.Items.Add(str);
            }
        }

        /// <summary>
        /// 设置波特率
        /// </summary>
        public static void SetBauRateValues(ComboBox obj)
        {
            obj.Items.Clear();
            obj.Items.Add("115200");
            obj.Items.Add("9600");
            obj.Items.Add("4800");
            obj.Items.Add("custom");
        }

        /// <summary>
        /// 设置数据位
        /// </summary>
        public static void SetDataBitValues(ComboBox obj)
        {
            obj.Items.Clear();
            obj.Items.Add("8");
            obj.Items.Add("7");
            obj.Items.Add("6");
            obj.Items.Add("5");
        }

        /// <summary>
        /// 设置停止位
        /// </summary>
        public static void SetStopBitValues(ComboBox obj)
        {
            obj.Items.Clear();
            obj.Items.Add("1");
            obj.Items.Add("1.5");
            obj.Items.Add("2");
        }

        /// <summary>
        /// 设置校验位列表
        /// </summary>
        public static void SetParityValues(ComboBox obj)
        {
            obj.Items.Clear();
            obj.Items.Add("无");
            obj.Items.Add("奇检验");
            obj.Items.Add("偶检验");
        }

        /// <summary>
        /// 端口是否已经打开
        /// </summary>
        public bool IsOpen()
        {
            return comPort.IsOpen;
        }

        /// <summary>
        /// 打开端口
        /// </summary>
        /// <returns></returns>
        public bool OpenPort(string name, string baud, string dBits, string sBits, string par)
        {
            if ((name == "") || (baud == "") || (dBits == "") || (sBits == "") || (par == ""))
            {
                MessageBox.Show("请选择串口");
                return false;
            }

            //中文不再乱码
            comPort.Encoding = Encoding.GetEncoding("GB18030");


            if (comPort.IsOpen)
                comPort.Close();

            comPort.PortName = name;

            comPort.BaudRate = Convert.ToInt32(baud);

            comPort.DataBits = Convert.ToInt16(dBits); ;

            if (sBits == "1.5")
            {
                comPort.StopBits = StopBits.OnePointFive;
            }
            else if (sBits == "1")
            {
                comPort.StopBits = StopBits.One;
            }
            else if (sBits == "2")
            {
                comPort.StopBits = StopBits.Two;
            }

            if (par == "无")
            {
                comPort.Parity = Parity.None;
            }
            else if (par == "奇校验")
            {
                comPort.Parity = Parity.Odd;
            }
            else if (par == "偶校验")
            {
                comPort.Parity = Parity.Even;
            }

            try
            {
                comPort.Open();
            }
            catch
            {
                MessageBox.Show("打开串口失败");
                return false;
            }
            return true;
        }

        /// <summary>
        /// 关闭端口
        /// </summary>
        public bool ClosePort()
        {
            try
            {
                if (comPort.IsOpen)
                    comPort.Close();
            }
            catch
            {
                MessageBox.Show("关闭串口失败");
                return false;
            }
            return true;
        }



        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="msg">包含要写入端口的字节数组</param>
        /// <param name="offset">参数从0字节开始的字节偏移量</param>
        /// <param name="count">要写入的字节数</param>
        public void Write(byte[] msg, int offset, int count)
        {
            comPort.Write(msg, offset, count);
        }

        public void SendDataByString(string data)
        {
            if (comPort.IsOpen == true)
            {
                try
                {
                    byte[] bytes = Encoding.GetEncoding("GB2312").GetBytes(data);
                    int length = bytes.Length;

                    TxByte += length;

                    comPort.Write(bytes, 0, length);
                }
                catch (Exception)
                {
                    MessageBox.Show("发送数据时发生错误, 串口将被关闭！", "错误提示");
                    comPort.Close();
                }
            }
            else
            {
                MessageBox.Show("串口未打开，请打开串口！", "错误提示");
            }
        }

        public void SendDataByHex(string str)
        {
            string print_str = str;
            string[] strArray = str.Split(new char[] { ' ' });
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < strArray.Length; i++)
            {
                builder.Append(strArray[i]);
            }
            str = builder.ToString();
            if ((str.Length % 2) != 0)
            {
                str = str.Insert(str.Length - 1, "0");
            }
            byte[] buffer = new byte[str.Length / 2];

            for (int j = 0; j < (str.Length / 2); j++)
            {
                try
                {
                    buffer[j] = Convert.ToByte(str.Substring(j * 2, 2), 0x10);
                }
                catch
                {
                    //停止循环发送 防止无限弹窗
                    //checkBoxLoopSend.Checked = false;
                    MessageBox.Show("包含非16进制字符，发送失败！", "提示");
                    return;
                }
            }

            if (comPort.IsOpen == true)
            {
                try
                {
                    TxByte += buffer.Length;

                    string print_time = DateTime.Now.ToString("HH:mm:ss.fff");
                    //richText_sp.AppendText("[" + print_time + "] -> " + print_str + "\r\n");

                    comPort.Write(buffer, 0, buffer.Length);
                }
                catch (Exception)
                {
                    MessageBox.Show("发送数据时发生错误, 串口将被关闭！", "错误提示");
                    comPort.Close();
                }
            }
            else
            {
                MessageBox.Show("串口未打开，请打开串口！", "错误提示");
            }
        }

        //从缓冲区读取数据
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (SpClosing) return;//如果正在关闭，忽略操作，直接返回，尽快的完成串口监听线程的一次循环
            try
            {
                Listening = true;//设置标记，说明我已经开始处理数据，一会儿要使用系统UI的。

                //启动关闭流程 则不再读取缓存中的数据
                if (flag_close == true)
                    return;

                if (HexRecvFlag == true)
                {
                    //要接收16进制 必须使用byte接收
                    int read_len = comPort.BytesToRead;
                    byte[] SpRecvData = new byte[read_len];
                    comPort.Read(SpRecvData, 0, read_len);
                    RxByte += SpRecvData.Length;
                    updateTextBox(SpRecvData);
                }
                else
                {
                    string SpRecvString;
                    //接收字符串使用 该方法 防止汉字乱码
                    SpRecvString = comPort.ReadExisting();
                    byte[] SpRecvData = Encoding.Default.GetBytes(SpRecvString);
                    RxByte += SpRecvData.Length;
                    updateTextBox(SpRecvData);
                }
                
            }
            finally
            {
                Listening = false;//我用完了，ui可以关闭串口了。
            }
        }
    }
}
